home *** CD-ROM | disk | FTP | other *** search
/ Creative Computers / Creative Computers CD-ROM, Volume 1 (Legendary Design Technologies, Inc.)(1994).iso / shareware / graphics / flick_1.0 / source / median.c < prev    next >
C/C++ Source or Header  |  1994-11-17  |  7KB  |  217 lines

  1. /*****************************************************************************
  2.  
  3.         Flick FLI-format Animation Viewer v1.0          19 Dec 1993
  4.         --------------------------------------
  5.  
  6.  
  7. This program plays 320x200x8 FLI-format bitmapped animation files on
  8. any ECS or AGA Amiga running OS2.04 or higher.  FLI-format files are
  9. produced by Autodesk Animator and Autodesk 3D Studio on a PC, as well
  10. as by other programs.
  11.  
  12. The files in this archive may be distributed anywhere provided they are
  13. unmodified and are not sold for profit.
  14.  
  15. Ownership and copyright of all files remains with the author:
  16.  
  17.     Peter McGavin, 86 Totara Crescent, Lower Hutt, New Zealand.
  18.     e-mail: peterm@maths.grace.cri.nz
  19.  
  20. *****************************************************************************/
  21.  
  22. #include "includes.h"
  23.  
  24. #define NBITS 4            /* for each r, g, b */
  25. #define NSIDE (1<<NBITS)
  26. #define MAX_LIST 32
  27. #define NPALETTE 256      /* # of palette entries */
  28.  
  29. typedef enum rgb_type {R, G, B};
  30.  
  31. struct box_type {                  /* Defines a parallelopiped */
  32.   UBYTE start[3];
  33.   UBYTE end[3];
  34. };
  35.  
  36. struct node_type {
  37.   struct box_type box;
  38.   UWORD n;
  39.   enum rgb_type long_primary;
  40.   ULONG badness;
  41.   UWORD primaryhist[3][NSIDE];
  42.   UBYTE palette[3];
  43. };
  44.  
  45.  
  46. static UWORD hist[NSIDE][NSIDE][NSIDE];    /* 3D histogram */
  47.  
  48. static void update_entry (struct node_type *node)
  49. {
  50.   UWORD r, g, b, i, mean;
  51.   ULONG c, wsum, bad;
  52.   LONG signed_diff;
  53.   enum rgb_type primary;
  54.  
  55.   /* build histogram for each primary */
  56.   memset (node->primaryhist, 0, 3 * NSIDE * sizeof(UWORD));
  57.   node->n = 0;
  58.   for (r = node->box.start[R]; r <= node->box.end[R]; r++)
  59.     for (g = node->box.start[G]; g <= node->box.end[G]; g++)
  60.       for (b = node->box.start[B]; b <= node->box.end[B]; b++) {
  61.         c = hist[r][g][b];
  62.         node->primaryhist[R][r] += c;
  63.         node->primaryhist[G][g] += c;
  64.         node->primaryhist[B][b] += c;
  65.         node->n += c;
  66.       }
  67.  
  68.   if (node->n == 0)
  69.     die ("Empty box!  This should never happen.\n");
  70.  
  71.   /* shrink the box */
  72.   for (primary = R; primary <= B; primary++) {
  73.     i = node->box.start[primary];
  74.     while (node->primaryhist[primary][i] == 0)
  75.       i++;
  76.     node->box.start[primary] = i;
  77.     i = node->box.end[primary];
  78.     while (node->primaryhist[primary][i] == 0)
  79.       i--;
  80.     node->box.end[primary] = i;
  81.   }
  82.  
  83.   /* compute the badness & choose the primary with the greatest badness */
  84.   node->badness = 0;
  85.   for (primary = R; primary <= B; primary++) {
  86.     wsum = 0;
  87.     for (i = node->box.start[primary]; i <= node->box.end[primary]; i++)
  88.       wsum += (node->primaryhist[primary][i] * (ULONG)i);
  89.     mean = ((wsum << 1) + node->n) / (node->n << 1);
  90.     node->palette[primary] = (UBYTE)(mean << 4);
  91.     bad = 0;
  92.     for (i = node->box.start[primary]; i <= node->box.end[primary]; i++) {
  93.       signed_diff = ((WORD)mean) - ((WORD)(i));
  94.       bad += node->primaryhist[primary][i] * (ULONG)(signed_diff * signed_diff);
  95.     }
  96.     if (bad >= node->badness) {
  97.       node->badness = bad;
  98.       node->long_primary = primary;
  99.     }
  100.   }
  101. }
  102.  
  103.  
  104. static void cut (struct node_type *node0, struct node_type *node1)
  105. {
  106.   ULONG sum, goal;
  107.   UWORD split;
  108.  
  109.   if (node0->badness == 0)
  110.     die ("Badness == 0!  This should never happen\n");
  111.  
  112.   /* find the median */
  113.   goal = node0->n >> 1;
  114.   sum = 0;
  115.   split = node0->box.start[node0->long_primary];
  116.   while (sum <= goal)
  117.     sum += node0->primaryhist[node0->long_primary][split++];
  118.  
  119.   /* go back a bit if necessary to get a better balance */
  120.   if ((sum - goal) >
  121.       (goal - (sum - node0->primaryhist[node0->long_primary][split - 1])))
  122.     sum -= node0->primaryhist[node0->long_primary][--split];
  123.  
  124.   /* copy box */
  125.   node1->box = node0->box;
  126.  
  127.   /* set new dimensions of boxes */
  128.   node0->box.end[node0->long_primary] = split - 1;
  129.   node1->box.start[node0->long_primary] = split;
  130.  
  131.   /* update the nodes */
  132.   update_entry (node0);
  133.   update_entry (node1);
  134. }
  135.  
  136.  
  137. static struct node_type node[32];          /* node list */
  138.  
  139. void median_cut (UBYTE colourtable[NPALETTE][3],
  140.                  UWORD *viewcolourtable,
  141.                  UBYTE *xlate)
  142. {
  143.   UWORD idx, this_idx, worst_idx, c, r, g, b, best_idx;
  144.   WORD dr, dg, db;
  145.   ULONG max_badness, max_n, dist, best_dist;
  146.   enum rgb_type primary;
  147.  
  148.   /* build histogram from colourtable (unlike conventional median split
  149.      algorithm which uses pixels from image, but that would be too slow) */
  150.   memset (hist, 0, NSIDE * NSIDE * NSIDE * sizeof(UWORD));
  151.   for (c = 0; c < NPALETTE; c++) {
  152.     r = colourtable[c][R] >> 4;
  153.     g = colourtable[c][G] >> 4;
  154.     b = colourtable[c][B] >> 4;
  155.     if (r < 8 && g < 8 && b < 8)    /* use EHB if possible */
  156.       hist[r << 1][g << 1][b << 1]++;
  157.     else
  158.       hist[r][g][b]++;
  159.   }
  160.  
  161.   /* set up an initial box containing the entire colour cube */
  162.   for (primary = R; primary <= B; primary++) {
  163.     node[0].box.start[primary] = 0;
  164.     node[0].box.end[primary] = NSIDE - 1;
  165.   }
  166.   update_entry (&node[0]);
  167.  
  168.   /* locate and subdivide the worst box until there are not enough EHB
  169.      palette entries for more boxes or all the boxes are too small to
  170.      subdivide further */
  171.   for (this_idx = 1; this_idx < 32; this_idx++) {
  172.     max_badness = 0;
  173.     max_n = 0;
  174.     for (idx = 0; idx < this_idx; idx++)
  175.       if (node[idx].badness > max_badness ||
  176.           (node[idx].badness == max_badness && node[idx].n > max_n)) {
  177.         max_badness = node[idx].badness;
  178.         max_n = node[idx].n;
  179.         worst_idx = idx;
  180.       }
  181.     if (max_badness == 0)
  182.       break;
  183.     cut (&node[worst_idx], &node[this_idx]);
  184.   }
  185.  
  186.   /* fill the viewcolourtable */
  187.   for (idx = 0; idx < this_idx; idx++)
  188.     viewcolourtable[idx] = ((node[idx].palette[R] >> 4) << 8) |
  189.                            ((node[idx].palette[G] >> 4) << 4) |
  190.                             (node[idx].palette[B] >> 4);
  191.  
  192.   /* calculate pixel translation table */
  193.   for (c = 0; c < NPALETTE; c++) {
  194.     r = colourtable[c][R];
  195.     g = colourtable[c][G];
  196.     b = colourtable[c][B];
  197.     best_dist = 32760;
  198.     for (idx = 0; idx < this_idx; idx++) {
  199.       dr = ((WORD)r) - (WORD)node[idx].palette[R];
  200.       dg = ((WORD)g) - (WORD)node[idx].palette[G];
  201.       db = ((WORD)b) - (WORD)node[idx].palette[B];
  202.       if ((dist = dr * dr + dg * dg + db * db) < best_dist) {
  203.         best_dist = dist;
  204.         best_idx = idx;
  205.       }
  206.       dr = ((WORD)r) - (WORD)(node[idx].palette[R] >> 1);
  207.       dg = ((WORD)g) - (WORD)(node[idx].palette[G] >> 1);
  208.       db = ((WORD)b) - (WORD)(node[idx].palette[B] >> 1);
  209.       if ((dist = dr * dr + dg * dg + db * db) < best_dist) {
  210.         best_dist = dist;
  211.         best_idx = idx + 32;    /* EHB colour entry */
  212.       }
  213.     }
  214.     xlate[c] = best_idx;
  215.   }
  216. }
  217.